﻿using Microsoft.Xna.Framework;
using System;

namespace OmegaEngine
{
    // Basic Camera Class
    public class Camera : Component, I3DComponent
    {
        // Internal Values
        Vector3 position = Vector3.Zero;
        Matrix rotationMatrix = Matrix.Identity;
        Vector3 target = new Vector3(0, 0, -1);
        Vector3 offset = Vector3.Zero;
        public Vector3 forward = Vector3.Forward;
        public Vector3 right = Vector3.Right;
        public Vector3 up = Vector3.Up;
        Matrix view = new Matrix();
        Matrix projection = new Matrix();

        // The offset of the camera
        public virtual Vector3 Offset
        {
            get { return offset; }
            set { offset = value; } 
        }

        // The point the camera is looking at
        public virtual Vector3 Target
        {
            get { return target; } set { target = value; }
        }

        // The View and Projection Matrices commonly used for rendering
        public virtual Matrix View
        {
            get { return view; } set { view = value; }
        }

        public virtual Matrix Projection
        {
            get { return projection; } set { projection = value; }
        }

        // Up vector
        public virtual Vector3 Up
        {
            get { return up; } set { up = value; }
        }

        // Public I3DComponent value
        public virtual Vector3 Position
        {
            get { return position; } set { position = value; }
        }

        public virtual Vector3 Scale
        {
            get { return Vector3.One; } set {}
        }

        public virtual void RotateTranslate(Vector3 Rotation, Vector3 Translation)
        {

        }

        public Vector3 EulerRotation
        {
            get { return MathUtil.MatrixToVector3(rotationMatrix); }
            set { rotationMatrix = MathUtil.Vector3ToMatrix(value); }
        }

        // The rotation matrix used by the camera and the current up vector
        public virtual Matrix Rotation
        {
            get { return rotationMatrix; } set { rotationMatrix = value; }
        }

        public virtual BoundingBox BoundingBox
        {
            get { return new BoundingBox(Position - Vector3.One, Position + Vector3.One); }
        }

        // Constructors
        public Camera(GameScreen Parent) : base(Parent) {}
        public Camera() : base() {}

        // Update the Camera
        public override void Update()
        {
            // Calculate the direction from the position to the target, and normalize
            Vector3 newForward = Target - Position;
            this.forward = newForward;
            newForward.Normalize();

            // Set the rotation matrix's forward to this vector
            Matrix rotationMatrixCopy = this.Rotation;
            rotationMatrixCopy.Forward = newForward;

            // Save a copy of "Up" ( 0, 1, 0 )
            Vector3 referenceVector = this.up;//Vector3.Up;

            // On the slim chance that the camera is point perfectly parallet with the Y axis, we cannot use cross product with a parallel
            // axis, so we change the reference vector to the forward axis (Z).
            if(rotationMatrixCopy.Forward.Y == referenceVector.Y || rotationMatrixCopy.Forward.Y == -referenceVector.Y)
                referenceVector = Vector3.Backward;

            // Calculate the other parts of the rotation matrix
            rotationMatrixCopy.Right = Vector3.Cross(this.Rotation.Forward, referenceVector);
            rotationMatrixCopy.Up = Vector3.Cross(this.Rotation.Right, this.Rotation.Forward);

            this.Rotation = rotationMatrixCopy;

            // Use the rotation matrix to find the new up
            Up = Rotation.Up;

            //
            // Target = Vector3.Transform(Target, Rotation);

            // Recalculate View and Projection using the new Position, Target, and Up
            View = Matrix.CreateLookAt(Position, Target, Up);
            Projection = MathUtil.CreateProjectionMatrix();
        }
    }
}